﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using GMBase.UI;

    /// <summary>
    /// 窗口管理器
    /// </summary>
    public class WindowMgr : SingletonAuto<WindowMgr>
    {
        class WinLayerHolderList : IEnumerable
        {
            private WindowLayerHolder[] _Layers;
            public WinLayerHolderList()
            {
                _Layers = new WindowLayerHolder[GWindow.LayerCount];
            }

            public int Length
            {
                get { return _Layers.Length; }
            }
            public int Count
            {
                get { return _Layers.Length; }
            }
            public WindowLayerHolder this[int index]
            {
                get
                {
                    return _Layers[index + 1];
                }
                set
                {
                    _Layers[index + 1] = value;
                }
            }
            public WindowLayerHolder this[WindowLayer layer]
            {
                get { return _Layers[(int)layer + 1]; }
                set { _Layers[(int)layer + 1] = value; }
            }
            public IEnumerator GetEnumerator()
            {
                return _Layers.GetEnumerator();
            }
        }
        /// <summary>
        /// 窗口层级隐藏信息
        /// </summary>
        struct stHideInfo
        {
            /// <summary>
            /// 隐藏优先级
            /// </summary>
            public int priority;
            /// <summary>
            /// 开始层级
            /// </summary>
            public int layerStart;
            /// <summary>
            /// 结束层级（含）
            /// </summary>
            public int layerEnd;
            /// <summary>
            /// 隐藏请求者
            /// </summary>
            public object hidder;

            /// <summary>
            /// 判断给定层级是否属于当前的隐藏层级
            /// </summary>
            /// <param name="layer"></param>
            /// <returns></returns>
            public bool IsLayerBetween(int layer)
            {
                return layer >= layerStart && layer <= layerEnd;
            }
        }


        private float _LastCSGCTime = 0;
        private float _LastNativeGCTime = 0;
        private float _LastLuaGCTime = 0;

        private GameObject _Root;
        private GameObject _PoolRoot;
        private Camera _UICamera;
        private RectTransform _UIRectTrans;
        private WinLayerHolderList _LayerHolders = new WinLayerHolderList();
        private HashSet<GWindow> _FullScreenWindows = new HashSet<GWindow>();

        private List<stHideInfo> _HideList = new List<stHideInfo>();

        private PointerEventData _RayCastPointer;
        private List<RaycastResult> _RayCastResults;

        public WindowMgr()
        {
            _Root = GameObject.Find("Canvas");
            U3DUtil.DestroyGameObject(_Root);
            _Root = AssetMgr.me.LoadGuiObject("Main/GameCanvas.prefab");
            _UICamera = _Root.transform.FindChild("UICamera").GetComponent<Camera>();
            //YMaskableGraphic.s_RootCanvas = _Root.transform;
            _PoolRoot = new GameObject("Pool");
            _PoolRoot.layer = LayerIndex.UI;
            _PoolRoot.transform.SetParent(_Root.transform);
            _PoolRoot.SetActive(false);

            for (int i = GWindow.LayerStart; i <= GWindow.LayerEnd; ++i)
            {
                WindowLayer layerEnum = (WindowLayer)i;
                string layerName = System.Enum.GetName(typeof(WindowLayer), layerEnum);
                GameObject layerRoot = U3DUtil.GetChildByName(_Root, layerName, true);
                if (layerRoot == null)
                {
                    layerRoot = new GameObject(layerName);
                    layerRoot.transform.SetParent(_Root.transform);
                    layerRoot.transform.position = new Vector3(0.0f, 0.0f, 100.0f);
                }
                WindowLayerHolder winLayer = new WindowLayerHolder(layerRoot, i);
                _LayerHolders[i] = winLayer;
            }
            //if (Game.gameRunning)
            //{
            //    GameObject.DontDestroyOnLoad(_Root);
            //}
            _UIRectTrans = GetLayerRoot((int)WindowLayer.Screen).GetComponent<RectTransform>();
            U3DUtil.StampHideFlags(_Root, HideFlags.DontSaveInEditor);
        }

        /// <summary>
        /// GUI根节点
        /// </summary>
        public GameObject Root
        {
            get { return _Root; }
        }
        /// <summary>
        /// 回收池的根节点
        /// </summary>
        public GameObject poolRoot
        {
            get { return _PoolRoot; }
        }
        /// <summary>
        /// ui使用的唯一camera
        /// </summary>
        public Camera uiCamera
        {
            get { return _UICamera; }
        }
        /// <summary>
        /// 整个界面的缩放系数
        /// </summary>
        public float scaleFactor
        {
            get
            {
                return _UIRectTrans.localScale.x;
            }
        }
        /// <summary>
        /// 屏幕宽
        /// </summary>
        public float sceneWidth
        {
            get
            {
                return _UIRectTrans.rect.width;
            }
        }
        /// <summary>
        /// 屏幕高
        /// </summary>
        public float sceneHeight
        {
            get
            {
                return _UIRectTrans.rect.height;
            }
        }
        /// <summary>
        /// 是否显示界面
        /// </summary>
        public bool uiVisible
        {
            get
            {
                return _Root.activeSelf;
            }
            set
            {
                _Root.SetActive(value);
            }
        }

        /// <summary>
        /// 当前是否有窗口层级在隐藏中
        /// </summary>
        public bool hidden
        {
            get { return _HideList.Count > 0; }
        }
        /// <summary>
        /// 窗口隐藏调用者数量
        /// </summary>
        public int hiddenNum
        {
            get { return _HideList.Count; }
        }

        /// <summary>
        /// 判断给定层级是否在隐藏中
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public bool IsLayerHidden(int layer)
        {
            return _LayerHolders[layer].cull;
        }
        /// <summary>
        /// 判断给定层级是否在隐藏中
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        
        public bool IsLayerHidden(WindowLayer layer)
        {
            return _LayerHolders[(int)layer].cull;
        }

        /// <summary>
        /// 查询当前鼠标位置下面的ui对象
        /// </summary>
        /// <returns>ui对象</returns>
        public GameObject QueryUIObjectUnderPointer()
        {
            Vector3 mousePos = Input.mousePosition;
            return QueryUIObject(mousePos.x, mousePos.y);
        }
        /// <summary>
        /// 查询给定位置下的ui对象
        /// </summary>
        /// <param name="x">屏幕位置x</param>
        /// <param name="y">屏幕位置y</param>
        /// <returns>ui对象</returns>
        public GameObject QueryUIObject(float x, float y)
        {
            if (_RayCastResults == null)
                _RayCastResults = new List<RaycastResult>();
            if (_RayCastPointer == null)
                _RayCastPointer = new PointerEventData(EventSystem.current);
            _RayCastPointer.position = new Vector2(x, y);
            for (int i = GWindow.LayerEnd; i >= GWindow.LayerStart; --i)
            {
                var lyer = _LayerHolders[i];
                lyer.Raycast(_RayCastPointer, _RayCastResults);
                if (_RayCastResults.Count > 0)
                {
                    var objRet = _RayCastResults[0].gameObject;
                    _RayCastResults.Clear();
                    return objRet;
                }
            }
            return null;
        }
        
        private WindowLayerHolder GetLayerHolder(int layer)
        {
            return _LayerHolders[layer];
        }
        private WindowLayerHolder GetLayerHolder(WindowLayer layer)
        {
            return _LayerHolders[(int)layer];
        }
        /// <summary>
        /// 获得挂载层级
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public GameObject GetLayerRoot(int layer)
        {
            return _LayerHolders[layer].root;
        }

        /// <summary>
        /// 向某个层级添加一个对象
        /// </summary>
        /// <param name="obj">待添加的对象</param>
        /// <param name="layer">层级索引</param>
        /// <param name="applyCull">是否需要自动应用cull</param>
        public void AddGameObject(GameObject obj, int layer, bool applyCull)
        {
            GetLayerHolder(layer).AddGameObject(obj, applyCull ? CullApplyMode.None : CullApplyMode.WhenCulling);
        }

        /// <summary>
        /// 向UI场景层级添加一个对象
        /// </summary>
        /// <param name="obj">待添加的对象</param>
        /// <param name="subLayer">子层级索引</param>
        /// <param name="applyCull">是否需要自动应用cull</param>
        public void AddSceneGameObject(GameObject obj, int subLayer, bool applyCull)
        {
            GetLayerHolder(WindowLayer.Scene).AddGameObject(obj, subLayer, applyCull ? CullApplyMode.WhenCulling : CullApplyMode.None);
        }

        /// <summary>
        /// 向游戏GUI的Canvas里添加一个窗口
        /// </summary>
        /// <param name="win">待添加的窗口</param>
        /// <param name="applyCull">是否自动应用cull</param>
        public void AddWindow(GWindow win, bool applyCull)
        {
            GetLayerHolder(win.Layer).AddWindow(win, applyCull ? CullApplyMode.WhenCulling : CullApplyMode.None);
        }

        /// <summary>
        /// 将窗口添加到父窗口上
        /// </summary>
        /// <param name="win">窗口</param>
        /// <param name="parentWin">父窗口</param>
        /// <param name="parentRoot">窗口挂载的目标父节点，传null表示使用窗口根节点</param>
        /// <param name="applyCull">是否自动应用cull</param>
        public void AddSubWindow(GWindow win, GWindow parentWin, GameObject parentRoot, bool applyCull)
        {
            var lyer = GetLayerHolder(parentWin.Layer);
            var pt = parentRoot != null ? parentRoot.transform : parentWin.Root.transform;
            RemoveWindow(win, applyCull ? lyer.cull : false, pt);
            lyer.AddWindow(win, parentWin, parentRoot, applyCull ? CullApplyMode.WhenCulling : CullApplyMode.None);
        }

        /// <summary>
        /// 向场景层增加window
        /// </summary>
        /// <param name="win"></param>
        /// <param name="subLayerIndex">场景子层的索引</param>
        public void AddSceneWindow(GWindow win, int subLayerIndex, bool applyCull)
        {
            GetLayerHolder(WindowLayer.Scene).AddWindow(win, subLayerIndex, applyCull ? CullApplyMode.WhenCulling : CullApplyMode.None);
        }

        /// <summary>
        /// 移除给定win
        /// </summary>
        /// <param name="win"></param>
        /// <param name="destToCull"></param>
        /// <param name="destRoot"></param>
        public void RemoveWindow(GWindow win, bool destToCull, Transform destRoot)
        {
            GetLayerHolder(win.Layer).RemoveWindow(win, destToCull, destRoot);
        }

        /// <summary>
        /// 删除一个窗口
        /// </summary>
        /// <param name="win">待删除的窗口</param>
        public void DeleteWindow(GWindow win)
        {
            GetLayerHolder(win.Layer).DestroyWindow(win);
        }

        /// <summary>
        /// 设置一个窗口的可见性
        /// </summary>
        /// <param name="win">窗口</param>
        /// <param name="visible">是否可见</param>
        public void ShowWindow(GWindow win, bool visible)
        {
            if (win.Visible == visible)
                return;
            win.gameObject.SetActive(visible);
        }

        /// <summary>
        /// 更换window的层级
        /// </summary>
        /// <param name="win"></param>
        /// <param name="destLayer">目标层级</param>
        public void ChangeWindowLayer(GWindow win, int destLayer)
        {
            RemoveWindow(win, IsLayerHidden(destLayer), null);
            win.Layer = (WindowLayer)destLayer;
            AddWindow(win, false);
        }

        /// <summary>
        /// 将给定窗口移动到它所在层级的最顶部（注意层级不会改变）
        /// </summary>
        /// <param name="win">窗口</param>
        public void MoveWindowFront(GWindow win)
        {
            GetLayerHolder(win.Layer).MoveWindowUpDown(win, -1);
        }

        /// <summary>
        /// 将给定窗口移动到它所在层级的最底部（注意层级不会改变）
        /// </summary>
        /// <param name="win"></param>
        public void MoveWindowBottom(GWindow win)
        {
            GetLayerHolder(win.Layer).MoveWindowUpDown(win, 0);
        }

        /// <summary>
        /// 通过layout加载一个窗口
        /// </summary>
        /// <param name="layoutName">layout文件名</param>
        /// <param name="funcID">功能ID，如果非0，则表示layout文件是在 7UI/funcID/layout/ 下的</param>
        /// <returns>窗口，如果加载失败，抛出异常</returns>
        public GWindow LoadWindow(string layoutName)
        {
            GameObject obj = AssetMgr.me.LoadGuiObject(layoutName);
            GWindow win = obj.GetComponent<GWindow>();
            if (win == null)
            {
                Debug.LogError(string.Format("Layout with name \"{0}\" has no GWindow script!", layoutName));
                return null;
            }
            return win;
        }
    }

